/*
Copyright (C) 2012-2013 John Tsiombikas <nuclear@member.fsf.org>,
                        Ioannis Makris <imak@imak.gr>

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#ifndef LIBGLORY_H_
#define LIBGLORY_H_

#ifdef __ANDROID__
#include <EGL/egl.h>
#include <GLES2/gl2.h>

#else	/* !__ANDROID__ */

#ifdef __APPLE__
#include <OpenGL/gl.h>

#else	/* !__APPLE__ */

#ifdef WIN32
#include <windows.h>
#endif

#include <GL/gl.h>
#endif	/* __APPLE__ */
#endif	/* __ANDROID__ */


#ifdef __cplusplus
#define GLORY_DEFARG(name, val)		name = val
#else
#define GLORY_DEFARG(name, val)		name
#endif


/* mode bits for the last argument to glory_create_window */
#define GLORY_SINGLE		 0		/* placeholder for single-bufferred visual */
#define GLORY_DOUBLE		 1		/* request a double-buffered visual */
#define GLORY_DEPTH			(1<<1)	/* request a depth buffer */
#define GLORY_STENCIL		(1<<2)	/* request a stencil buffer */
#define GLORY_STEREO		(1<<3)	/* request a stereo-capable visual */
#define GLORY_MULTISAMPLE	(1<<4)	/* request a multisample framebuffer */

/* mode bits for the API type (default: classic) */
#define GLORY_GL32			(1<<9)
#define GLORY_GLES			(1<<10)
#define GLORY_GLES2			(1<<11)

/* pass this special value when you prefer setting the OpenGL context
 * attributes with the detailed glory_setattr interface instead
 */
#define GLORY_USE_ATTR		0xffffffff

/* --- callback types ---
 * All event callbacks are entered with the corresponding OpenGL context being current.
 * It is safe to issue OpenGL commands in all callbacks functions, unless explicitly
 * stated otherwise below.
 */

/* Create callback is called just after a window and its OpenGL context is created */
typedef void (*glory_create_callback_t)(void *cls);

/* Destroy callback is called before the window and its OpenGL context is destroyed */
typedef void (*glory_destroy_callback_t)(void *cls);

/* The display callback is called whenever the window needs repainting. This is
 * the only place you should perform drawing.
 */
typedef void (*glory_display_callback_t)(void *cls);

/* Reshape is called whenever the window size changes, and at least once for
 * each newly created window, just after the call to the create callback. */
typedef void (*glory_reshape_callback_t)(int width, int height, void *cls);

/* The keyboard callback is called whenever a key is pressed or released */
typedef void (*glory_keyboard_callback_t)(int key, int pressed, void *cls);

/* The mouse callback is called whenever a mouse button is pressed or released
 * over the window.
 * The touch id (for multitouch or multi-pointer devices) can be retreived by
 * calling glory_mouse_id().
 */
typedef void (*glory_mouse_callback_t)(int button, int pressed, int x, int y, void *cls);

/* The motion callback is called when the user drags the mouse around with any
 * mouse buttons held down.
 * The touch id (for multitouch or multi-pointer devices) can be retreived by
 * calling glory_mouse_id().
 */
typedef void (*glory_motion_callback_t)(int x, int y, void *cls);

/* Passive gets called whenever the mouse is moved without any button being
 * held.
 * The touch id (for multitouch or multi-pointer devices) can be retreived by
 * calling glory_mouse_id().
 *
 * Avoid setting this callback unless absolutely necessary, because it will
 * generate a huge volume of events whenever the mouse moves over the window. */
typedef void (*glory_passive_callback_t)(int x, int y, void *cls);

/* The space control callback gets called when we receive input from spatial
 * controllers such as spaceballs, accelerometers, and gyroscopes
 */
typedef void (*glory_space_callback_t)(int x, int y, int z, int rx, int ry, int rz, void *cls);

/* The button callback gets called whenever we have button presses/releases
 * from special hardware buttons such as the home button on mobile devices,
 * buttons on spaceballs, etc.
 */
typedef void (*glory_button_callback_t)(int bn, int pressed, void *cls);

/* Setting the idle callback will change the behaviour of the library. Instead
 * of blocking, to wait for events from the window system, it will poll for
 * events and after processing them will call this idle callback.
 *
 * Avoid setting this callback unless absolutely necessary, because it will
 * turn your program into busy-looping mode, consuming all possible CPU time
 * allocated to it by the system. */
typedef void (*glory_idle_callback_t)(void *cls);

/* symbolic names for each callback */
enum {
	GLORY_CREATE,
	GLORY_DESTROY,
	GLORY_DISPLAY,
	GLORY_RESHAPE,
	GLORY_KEYBOARD,
	GLORY_MOUSE,
	GLORY_MOTION,
	GLORY_PASSIVE,
	GLORY_SPACE,
	GLORY_BUTTON,
	GLORY_IDLE,

	GLORY_NUM_CALLBACKS
};

enum {
	GLORY_LEFT_BUTTON,
	GLORY_MIDDLE_BUTTON,
	GLORY_RIGHT_BUTTON
};

/* these values happen to coincide with X11 keysyms */
#define GLORY_KEY_LSHIFT	0xffe1
#define GLORY_KEY_RSHIFT	0xffe2
#define GLORY_KEY_LCONTROL	0xffe3
#define GLORY_KEY_RCONTROL	0xffe4
#define GLORY_KEY_LALT		0xffe9
#define GLORY_KEY_RALT		0xffea

/* for the glory_modifiers bitmask */
#define GLORY_MOD_SHIFT		1
#define GLORY_MOD_CONTROL	2
#define GLORY_MOD_ALT		4

/* pass this as the last argument to set_video_mode to leave
 * the refresh rate unspecified and let the library choose
 */
#define GLORY_RATE_ANY		(-1)

#ifdef __cplusplus
extern "C" {
#endif

int glory_init(void);
void glory_cleanup(void);

/* set and query the current video mode
 * passing GLORY_RATE_ANY as the last argument leaves refresh rate
 * unspecified.
 */
int glory_set_video_mode(int xsz, int ysz, int rate);

/* NOTE: any of the arguments can be null */
int glory_get_video_mode(int *xsz, int *ysz, int *rate);

/* detailed OpenGL context attribute setting */
void glory_attr_color(int rbits, int gbits, int bbits);
void glory_attr_doublebuf(int enable);
void glory_attr_depth(int dbits);
void glory_attr_stencil(int sbits);
void glory_attr_stereo(int enable);
void glory_attr_multisample(int samples);
void glory_attr_api(int glapi); /* { 0 (classic GL), GLORY_GL3, GLORY_GLES } */

/* create a window and return its id
 * last argument is a bitmask describing the properties of the OpenGL context
 * to be created, or GLORY_USE_ATTR if you prefer to set them with the detailed
 * glory_setattr functions instead.
 *
 * NOTE: C++ users may just skip the last argument if they prefer the setattr API
 */
int glory_create_window(int xsz, int ysz, unsigned int GLORY_DEFARG(mode, GLORY_USE_ATTR));

/* destroy a window by its id */
void glory_destroy_window(int win);

/* switch to fullscreen/windowed mode */
void glory_fullscreen(void);
void glory_windowed(void);

/* set/get the currently active window */
void glory_set_active(int id);
int glory_get_active(void);


/* resize the currently active window */
void glory_resize(int width, int height);

void glory_set_title(const char *str);

void glory_redisplay(void);
void glory_swap_buffers(void);

unsigned int glory_modifiers(void);

/* glory_mouse_id can be called from within a mouse/motion/passive callback
 * to retreive the multi-pointer/multitouch id for the current event.
 * The result is undefined if called outside of a mouse-related callback.
 */
int glory_mouse_id(void);

void glory_quit(void);

/* glory_process_events runs a single iteration of the event loop
 * and returns -1 if the user tried to close the window.
 *
 * XXX This function will only be available on some platforms.
 * Do not use for portable programs.
 */
int glory_process_events(void);

/* Start the event loop.
 * You may call glory_quit() to terminate the event loop
 */
void glory_event_loop(void);

/* callback stack */
void glory_push_callbacks(void);
void glory_pop_callbacks(void);
void glory_clear_callbacks(void);
void glory_set_callback(int idx, void (*func)(), void *GLORY_DEFARG(cls, 0));
void (*glory_get_callback(int idx))();
void *glory_get_callback_data(int idx);

/* convenience functions for setting each callback
 *
 * all of these are equivalent to calling glory_set_callback with
 * the appropriate callback enumeration value.
 */
void glory_create_callback(glory_create_callback_t func, void *GLORY_DEFARG(cls, 0));
void glory_destroy_callback(glory_destroy_callback_t func, void *GLORY_DEFARG(cls, 0));
void glory_display_callback(glory_display_callback_t func, void *GLORY_DEFARG(cls, 0));
void glory_reshape_callback(glory_reshape_callback_t func, void *GLORY_DEFARG(cls, 0));
void glory_keyboard_callback(glory_keyboard_callback_t func, void *GLORY_DEFARG(cls, 0));
void glory_mouse_callback(glory_mouse_callback_t func, void *GLORY_DEFARG(cls, 0));
void glory_motion_callback(glory_motion_callback_t func, void *GLORY_DEFARG(cls, 0));
void glory_passive_callback(glory_passive_callback_t func, void *GLORY_DEFARG(cls, 0));
void glory_space_callback(glory_space_callback_t func, void *GLORY_DEFARG(cls, 0));
void glory_button_callback(glory_button_callback_t func, void *GLORY_DEFARG(cls, 0));
void glory_idle_callback(glory_idle_callback_t func, void *GLORY_DEFARG(cls, 0));

#ifdef __cplusplus
}
#endif

#endif	/* LIBGLORY_H_ */
